#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#define BUFSIZ  1024
#define MAXPATH 1024

typedef struct ptrace_event{ 
     sigset_t  pe_signals; 
     events_t  pe_set_event; 
} ptrace_event_t;

pid_t           npid, cpid, pid;
int             status, errors=0, pathlength;
ptrace_event_t  *event_addr;
ptrace_state_t  *state_addr;
char            *buf_addr;
size_t          event_len, state_len;
int             filed[2];

child()
{
    int n, bar;

    close(filed[1]);
    /* Wait for parent to write to pipe */
    while ((n = read(filed[0], &bar, BUFSIZ)) == 0);

    /* Now the child can exec. */
    if (execlp("ls", "ls", (char *)0) < 0)   /* error during exec */
        printf("Child: exec failed\n");
    exit(0);
}

parent()
{
    close(filed[0]);

    /* Before child does an exec, attach it and set its event flag. */
    if (ptrace(PT_ATTACH,pid))  /* failed to attach process */
        printf("Parent: Failed to attach child\n");
    if (pid != wait(&status))   /* wait failed */
        printf("Parent: attach failed with wrong wait status\n");
    if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
        printf("Parent: SIGTRAP didn't stop child\n");

    /*
     * The child process has now stopped.  Set its event flag indicating
     * that it needs to trigger on a PTRACE_EXEC event.
     */
    event_addr->pe_set_event = PTRACE_EXEC;
    if (ptrace(PT_SET_EVENT_MASK, pid, event_addr, event_len))
        printf("Parent: PT_SET_EVENT_MASK ptrace request failed\n");
    if (pid != wait(&status))   /* wait failed */
        printf("Parent: wait() failed with wrong wait status\n");

    /*
     * Send the child a message so it can break out of the while loop.
     * Get it running so it can exec.
     */
    write(filed[1], "now run", 7);
    if (ptrace(PT_CONTIN, pid, 1, 0) != 0)
        printf("Parent: failed to get child process running\n");
    /*
     * Wait for the traced child to stop after the exec system call in
     * response to an exec event set in its ptrace_event structure.
     */
    if (pid != (npid = wait(&status)))   /* wait failed */
        printf("Parent: wait() failed with wrong status\n");
    if (!WIFSTOPPED(status))
        printf("Parent: invalid wait() completion\n");

    /*
     * Child has stopped; fetch its process state and examine state
     * information.
     */
    if (ptrace(PT_GET_PROCESS_STATE, pid, state_addr, state_len) < 0)
        printf("Parent: PT_GET_PROCESS_STATE ptrace request failed\n");
    if (pid != wait(&status))   /* wait failed */
        printf("Parent: wait() failed with wrong wait status\n");

    /* Check if the pathlength value returned is non-zero */
    if ((pathlength = state_addr->pe_path_len) == 0)

        printf("Parent: zero length pathname returned\n");

    /* Fetch exec'd file pathname and store it in the buffer. */
    if (ptrace(PT_GET_PROCESS_PATHNAME, pid, buf_addr, (pathlength+1))
        < 0){
        printf("Parent: Failed to get exec pathname\n");
    } else {
        printf("Parent: the exec pathname is %s\n", buf_addr);
        if (pid != wait(&status))   /* wait failed */
            printf("Parent: wait() failed with wrong status\n");
    }
}

main()
{
    event_len = sizeof(ptrace_event_t);
    state_len = sizeof(ptrace_state_t);
    event_addr = calloc(event_len, 1);
    state_addr = calloc(state_len, 1);
    buf_addr = calloc(MAXPATH, 1);
    pipe(filed);
    switch (pid = fork()) {
    case -1:
        exit(1);
    case 0:
        child();
        break;
    default:
        parent();
        break;
    }
}
